#include "Scene.hpp"
#include "vectorUtils.hpp"
#include  "colorUtils.hpp"

Vector3f reflectRay(const Vector3f& L, const Vector3f& N) { return 2 * N * N.dot(L) - L; }

double Scene::computeLighting(const Vector3f& p, const Vector3f& N, const Vector3f& V, const Sphere& sphere)
{
    double i = 0.0f;

    for (auto& light : this->lights) {
        if (light.type == LightType::Ambient) {
            i += light.intensity;
        } else {
            Vector3f L;
            double tMax = INFINITY;
            if (light.type == LightType::Directional) {
                L = light.position; // 归一化方向光 .normalized()
            } else {
                L = light.position - p; // 点光源指向 .normalized()
                tMax = 1.f;
            }
            // L = L.normalized();
            // 计算阴影
            auto [closestSphere, t] = this->closestIntersect(p, L, 0.001, tMax);
            // 如果阴影存在，则不会有漫反射和镜面反射
            if (closestSphere) {
                continue;
            }
            // 漫反射
            float ndotl = N.dot(L);
            if (ndotl > 0) {
                i += light.intensity * ndotl / (N.length() * L.length());
            }

            // 镜面反射
            int s = sphere.specular;
            if (s != -1) {
                Vector3f R = reflectRay(L, N).normalized(); // 反射方向
                float rv = R.dot(V);
                if (rv > 0) {
                    i += light.intensity * pow(rv / (R.length() * V.length()), s);
                }
            }
        }
    }

    return i;
}

std::tuple<Sphere*, double> Scene::closestIntersect(const Vector3f& origin, const Vector3f& direction, double minT,
                                                    double maxT)
{
    double closeT = INFINITY;
    Sphere* closeSpere = nullptr;
    for (auto& sphere : spheres) {
        auto [t1, t2] = intersectRaySpere(origin, direction, sphere);
        if (t1 >= minT && t1 < maxT && t1 < closeT) {
            closeT = t1;
            closeSpere = &sphere;
        }
        if (t2 >= minT && t2 < maxT && t2 < closeT) {
            closeT = t2;
            closeSpere = &sphere;
        }
    }
    return {closeSpere, closeT};
}

std::tuple<double, double> Scene::intersectRaySpere(const Vector3f& origin, const Vector3f& direction, Sphere& sphere)
{
    auto r = sphere.radius;
    auto co = origin - sphere.center;

    auto a = direction.dot(direction); // 平方数
    auto b = 2 * co.dot(direction);
    auto c = co.dot(co) - pow(r, 2);

    const auto discriminant = b * b - 4 * a * c;
    if (discriminant < 0) {
        return {INFINITY, INFINITY};
    }
    auto t1 = (-b + sqrt(discriminant)) / (2 * a);
    auto t2 = (-b - sqrt(discriminant)) / (2 * a);
    return {t1, t2};
}

Color Scene::traceRay(const Vector3f& origin, const Vector3f& direction, float minT, float maxT, uint8_t recursiveCnt)
{
    auto [closeSpere, closeT] = this->closestIntersect(origin, direction, minT, maxT);
    if (closeSpere == nullptr) {
        return this->bgColor;
    }

    // 射线与球面的交点
    Vector3f normal = {1, 1, 1};
    const auto p = origin + closeT * direction;
    // 计算法向量
    Vector3f N = p - closeSpere->center;
    N = N.normalized();
    const auto view = -1 * direction;
    // 参数V 表示物体指向相机的向量
    // 光照强度最多1.0
    // 不会出现某个颜色值大于255的情况，但是有镜面反射的情况下，可能会超出255，
    // 函数内部会有边界判断
    double intensity = this->computeLighting(p, N, view, *closeSpere);
    auto color = closeSpere->color * intensity;
    // return color;
    float r = closeSpere->reflective;
    if (r <= 0 || recursiveCnt <= 0) {
        return color;
    }
    // 计算反射后的颜色
    auto R = reflectRay(view, N);
    auto reflectColor = traceRay(p, R, 0.01 + recursiveCnt * 1e-4, INFINITY, recursiveCnt - 1);

    return reflectColor * r + color * (1 - r);
}
